#!/usr/bin/env python
# -*- coding: UTF-8 -*-

# contact_list_window.py 
#
#  Copyright (c) 2007 INdT (Instituto Nokia de Technologia)
#                2007 Kenneth Rohde Christiansen
#
#  Author(s): Kenneth Christiansen <kenneth.christiansen@gmail.com>
#             Marcelo Lira dos Santos <setanta@gmail.com>
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License as
#  published by the Free Software Foundation; either version 2 of the
#  License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
#  USA

import pygtk
pygtk.require("2.0")
import gtk
import gconf
import tapioca

from contact_list_view import ContactListView
from avatar_selector import AvatarSelectorWindow
from login_widget import LoginWidget
from vcard_popup import VCardPopup
from contact_list_action_bar import ContactListActionBar
from user_info_panel import UserInfoPanel
from dialogs import AboutDialog
from join_room_window import JoinRoomWindow
from log_view_window import LogViewWindow

from gettext import gettext as _

CONFIG_PATH = "/apps/ereseva"

class MenuBar(gtk.UIManager):
    """
    A menu bar using the UIManager.
    """


    def __init__(self, parent):
        """
        A constructor for the menu bar.
        """

        self.parent = parent

        actions = [
            ( "ActionsMenu", None, _("_Actions") ),

            ( "About", gtk.STOCK_ABOUT, \
                "_About", None, None, \
                self.on_about_menuitem_clicked),

            ( "HelpMenu", None, _("_Help") ),
            ( "EditMenu", None, _("_Edit") ),
            ( "RoomMenu", None, _("_Room") ),

            ( "Quit", gtk.STOCK_QUIT, \
                _("_Quit"), None, None, \
                self.on_quit_menuitem_clicked),

            ( "AddContact", gtk.STOCK_ADD, \
                _("_Add contact..."), None, None, \
                self.on_add_contact_menuitem_clicked),

            ( "ChangeAvatar", None, \
                _("_Change avatar..."), None, None, \
                self.on_change_avatar_menuitem_clicked),

            ( "Preferences", gtk.STOCK_PREFERENCES, \
                _("_Preferences"), None, None, \
                self.on_preferences_menuitem_clicked),

            ( "JoinNewRoom", gtk.STOCK_NEW, \
                _("_Join new..."), None, None, \
                self.on_join_new_room_menuitem_clicked),

            ( "ShowPrevConversations", gtk.STOCK_JUSTIFY_LEFT, \
                _("_Show Previous Conversations..."), None, None, \
                self.on_show_prev_conversations_menuitem_clicked),
        ]

        toggleactions = [
            ( "SortAfterName", None, \
                _("Soft after _name"), None, None, \
                self.on_sort_after_name_clicked),

            ( "ShowPictures", None, \
                _("Show _pictures"), None, None, \
                self.on_show_pictures_clicked),

            ( "ShowOfflineContacts", None, \
                _("Show _offline friends"), None, None, \
                self.on_show_offline_contacts_clicked),
        ]

        actiongroup = gtk.ActionGroup("MenubarActions")
        toggleactiongroup = gtk.ActionGroup("CheckActions")
        actiongroup.add_actions(actions)
        toggleactiongroup.add_toggle_actions(toggleactions)
        toggleactiongroup.get_action("ShowOfflineContacts").\
            set_menu_item_type(gtk.CheckMenuItem)

        toggle_action = toggleactiongroup.get_action('SortAfterName')
        toggle_action.set_active(self.parent.config_client.\
            get_bool(CONFIG_PATH + "/sort-after-name"))
        toggle_action = toggleactiongroup.get_action('ShowPictures')
        toggle_action.set_active(self.parent.config_client.\
            get_bool(CONFIG_PATH + "/show-pictures"))
        toggle_action = toggleactiongroup.get_action('ShowOfflineContacts')
        toggle_action.set_active(self.parent.config_client.\
            get_bool(CONFIG_PATH + "/show-offline-contacts"))

        gtk.UIManager.__init__(self)

        self.insert_action_group(actiongroup, -1)
        self.insert_action_group(toggleactiongroup, -1)

        self.add_ui_from_string( \
        '<ui>' \
        '  <menubar action="menubar" name="menubar">' \
        '    <menu action="ActionsMenu" name="ActionsMenu">' \
        '      <menuitem action="ShowPrevConversations" name="ShowPrevConversations"/>' \
        '      <separator name="sep0"/>' \
        '      <menuitem action="ChangeAvatar" name="ChangeAvatar"/>' \
        '      <menuitem action="AddContact" name="AddContact"/>' \
        '      <separator name="sep1"/>' \
        '      <menuitem action="SortAfterName" name="SortAfterName"/>' \
        '      <menuitem action="ShowPictures" name="ShowPictures"/>' \
        '      <menuitem action="ShowOfflineContacts" name="ShowOfflineContacts"/>' \
        '      <separator name="sep2"/>' \
        '      <menuitem action="Quit" name="Quit"/>' \
        '    </menu>' \
        '    <menu action="RoomMenu" name="RoomMenu">' \
        '      <menuitem action="JoinNewRoom" name="JoinNewRoom"/>' \
        '    </menu>' \
        '    <menu action="HelpMenu" name="HelpMenu">' \
        '      <menuitem action="About" name="About"/>' \
        '    </menu>' \
        '  </menubar>' \
        '</ui>')


    def new(self):
        self.ensure_update()


    def on_show_prev_conversations_menuitem_clicked(self, widget, *args):
        """
        This function shows the log view containing the logs
        of the previous conversation.
        """

        window = LogViewWindow(self.parent.main_app, self.parent.local_contact_store)


    def on_join_new_room_menuitem_clicked(self, widget, *args):
        """
        This function pops up the join new room dialog.
        """

        window = JoinRoomWindow()


    def on_add_contact_menuitem_clicked(self, widget, *args):
        """
        This function pops up the add contact dialog.
        """
        
        self.parent.show_add_contact()


    def on_change_avatar_menuitem_clicked(self, widget, *args):
        """
        This function pops up the avatar selector dialog.
        """

        self.parent.show_select_avatar_window()


    def on_preferences_menuitem_clicked(self, widget, *args):
        """
        This function pops up the configuration dialog.
        """

        self.preferences_window = PreferencesWindow(None)
        self.preferences_window.connect('preferences-changed', \
            self.on_preferences_changed)


    def on_sort_after_name_clicked(self, widget, *args):
        """
        This function changes the state of sort-after-name flag.
        """

        self.parent.config_client.set_bool(CONFIG_PATH + '/sort-after-name',
                widget.get_active())
        self.parent.refresh_contact_list()


    def on_show_offline_contacts_clicked(self, widget, *args):
        """
        This function changes the state of show-offline-contacts flag.
        """

        self.parent.config_client.set_bool(CONFIG_PATH + '/show-offline-contacts',
                widget.get_active())
        self.parent.refresh_contact_list()


    def on_show_pictures_clicked(self, widget, *args):
        """
        This function changes the state of show-pictures flag.
        """

        self.parent.config_client.set_bool(CONFIG_PATH + '/show-pictures',
            widget.get_active())
        self.parent.refresh_contact_list()


    def on_about_menuitem_clicked(self, widget, *args):
        """
        This function pops up an about dialog.
        """

        dialog = AboutDialog()

        dialog.run()
        dialog.destroy()


    def on_quit_menuitem_clicked(self, widget, *args):
        """
        This callback function closes the application by quitting 
        the GTK+ main loop.
        """

        self.parent.quit()


    def on_preferences_changed(self, widget, property):
        print 'on_preferences_changed'
        print property


    def set_connected(self, connected=False):
        change_avatar = self.get_widget("/menubar/ActionsMenu/ChangeAvatar")
        change_avatar.set_property("sensitive", connected)
        add_contact = self.get_widget("/menubar/ActionsMenu/AddContact")
        add_contact.set_property("sensitive", connected)


class ApplicationWindow(gtk.Window):
    """
    The main application window.
    """

    def __init__(self, main_app, local_contact_store):
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)

        self.main_app = main_app
        self.local_contact_store = local_contact_store 

        self.last_position = None
        self.avatar_window = None

        self.vcard_popup = VCardPopup(self.local_contact_store)
        self.vcard_popup.connect('chat-session-requested', \
            self.on_chat_session_requested)
        self.vcard_popup.connect('voip-session-requested', \
            self.on_voip_session_requested)

        self.vcard_popup_timeout_id = 0
        self.previous_id = None

        self.config_client = gconf.client_get_default ()
        self.config_client.add_dir (CONFIG_PATH, gconf.CLIENT_PRELOAD_NONE)

        self.set_property("icon-name", 'ereseva')
        self.set_title(_("Ereséva"))
        self.connect("delete-event", self.on_delete_event)

        self.user_info_panel = UserInfoPanel(self, self.main_app, \
                self.local_contact_store)

        self.user_info_panel_alig = gtk.Alignment(0.5, 0.5, 1, 1)
        self.user_info_panel_alig.set_padding(4, 4, 4, 4)
        self.user_info_panel_alig.add(self.user_info_panel)

        self.login_widget = LoginWidget()
        self.login_widget.connect('connection-requested', \
            self.main_app.connect_account)

        self.contact_list_widget = ContactListView(self.local_contact_store)
        self.contact_list_widget.connect('chat-session-requested', \
                self.on_chat_session_requested)
        self.contact_list_widget.connect('voip-session-requested', \
                self.on_voip_session_requested)
        self.contact_list_widget.connect('webcam-session-requested', \
                self.on_webcam_session_requested)
        self.contact_list_widget.connect('over-contact', \
                self.update_vcard_popup)
        self.contact_list_widget.connect('leave-contact', \
                self.hide_vcard_popup)
        self.contact_list_widget.connect('contact-selected', \
                self.on_contact_selected)

        self.separator1 = gtk.HSeparator()
        self.separator2 = gtk.HSeparator()

        self.actions_box = ContactListActionBar(self.local_contact_store)
        self.actions_box.connect('add-contact', self.on_add_contact)
        self.actions_box.connect('chat-session-requested', \
                self.on_chat_session_requested)
        self.actions_box.connect('voip-session-requested', \
                self.on_voip_session_requested)
        self.actions_box.connect('webcam-session-requested', \
                self.on_webcam_session_requested)

        self.actions_alignment = gtk.Alignment(0.5, 0.5, 1, 1)
        self.actions_alignment.set_padding(4, 4, 4, 4)
        self.actions_alignment.add(self.actions_box)

        self.menubar = MenuBar(self)

        #self.notebook = gtk.Notebook()
        #self.notebook.set_tab_pos(gtk.POS_TOP)

        #self.contact_list_page = gtk.VBox(False, 0)
        #self.empty_notebook_page = gtk.VBox(False, 0)

        #self.notebook.add(self.contact_list_page)
        #self.notebook.add(self.empty_notebook_page)
        #self.notebook.set_border_width(4)

        #self.contact_list_page.pack_start(self.contact_list_widget.get_widget(), expand=True, fill=True)
        #self.contact_list_page.pack_start(self.separator2, expand=False, fill=True)
        #self.contact_list_page.pack_start(self.actions_alignment, expand=False, fill=True)

        #self.contact_list_label = gtk.Label(_("Contact List"))
        #self.notebook.set_tab_label(self.notebook.get_nth_page(0), self.contact_list_label)

        #self.active_calls_label = gtk.Label(_("Active Calls"))
        #self.notebook.set_tab_label(self.notebook.get_nth_page(1), self.active_calls_label)

        # Need to use the switch page signal to not be able to open the page
        #self.active_calls_label.set_property("sensitive", False)

        #self.notebook.set_current_page(0)

        self.vbox = gtk.VBox(spacing=3)
        self.vbox.pack_start(self.menubar.get_widget("/menubar"), expand=False, fill=True)
        self.vbox.pack_start(self.user_info_panel_alig, expand=False, fill=True)
        self.vbox.pack_start(self.separator1, expand=False, fill=True)
        self.vbox.pack_start(self.login_widget.get_widget(), expand=True, fill=True)
        #self.vbox.pack_start(self.notebook, expand=True, fill=True)
        self.vbox.pack_start(self.contact_list_widget.get_widget(), expand=True, fill=True)
        self.vbox.pack_start(self.separator2, expand=False, fill=True)
        self.vbox.pack_start(self.actions_alignment, expand=False, fill=True)

        self.add(self.vbox)

        # Show everything except the window, then hide non-needed widgets
        # and then show the window
        self.vbox.show_all()
        self.set_disconnected_view()

        self.move_and_resize_to_saved_state()

        self.show()


    def move_and_resize_to_saved_state(self):
        """
        This function reads the saved x, y coordinates and the height
        and width from when the window was last shown.

        It then resized and moved to window accordently.
        """

        x_coord = self.config_client.get_int(CONFIG_PATH + '/x-coord')
        y_coord = self.config_client.get_int(CONFIG_PATH + '/y-coord')
        width = self.config_client.get_int(CONFIG_PATH + '/width')
        height = self.config_client.get_int(CONFIG_PATH + '/height')

        if width == 0:
            width = 320

        if height == 0:
            height = 450

        self.move(x_coord, y_coord)
        self.resize(width, height)


    def save_geometry_and_position_state(self):
        """
        This function saved the x and y coordinates of the position
        of the window as well as the height and width, and stores
        it in the gconf configuration to be reread as a later time.
        """

        x_coord, y_coord = self.get_position()
        width, height = self.get_size()

        self.config_client.set_int(CONFIG_PATH + '/width', width)
        self.config_client.set_int(CONFIG_PATH + '/height', height)
        self.config_client.set_int(CONFIG_PATH + '/x-coord', x_coord)
        self.config_client.set_int(CONFIG_PATH + '/y-coord', y_coord)


    def add_contacts(self, contacts):
        self.contact_list_widget.add_contacts(contacts)


    def show_add_contact(self):
        self.actions_box.show_add_contact()


    def update_pending_contacts(self, contact_id=None):
        if contact_id:
            self.actions_box.add_authorize_subscription(contact_id)
        else:
            pending = self.local_contact_store.get_pending_contacts()
            self.actions_box.init_authorize_subscription(pending)


    def on_add_contact(self, widget, contact_id):
        contact = self.main_app.contact_list.add(contact_id)
        contact.subscribe(True)
        contact.connect('subscription-status-changed', \
            self.on_subscription_status_changed)


    def on_subscription_status_changed(self, contact, status):
        self.contact_list_widget.add_contacts([contact])
        print '=== on_subscription_status_changed ==='
        print contact.get_alias()
        print status


    def show_select_avatar_window(self):
        if self.avatar_window == None:
            self.avatar_window = \
                    AvatarSelectorWindow(self.main_app.account)
            self.avatar_window.connect('avatar-selected', \
                    self.on_avatar_selected)
        else:
            self.avatar_window.present()


    def on_avatar_selected(self, widget, avatar_file):
        self.avatar_window = None

        if avatar_file == None:
            return

        avatar = tapioca.avatar_new_from_file(avatar_file, 'image/png')
        self.user_info_panel.set_user_avatar(avatar)


    def set_user_alias(self, alias):
        self.user_info_panel.set_user_alias(alias)


    def set_user_avatar(self, avatar):
        self.user_info_panel.set_user_avatar(avatar)


    def on_chat_session_requested(self, widget, contact=None):
        if not contact:
            contact = self._get_selected_contact()
        self.main_app.start_chat_session(contact)


    def on_voip_session_requested(self, widget, contact=None):
        print 'application_window.py:on_voip_session_requested ---------------------'
        if not contact:
            contact = self._get_selected_contact()

        self.main_app.start_voip_session(contact)


    def on_webcam_session_requested(self, widget, contact=None):
        if not contact:
            contact = self._get_selected_contact()
        self.main_app.start_webcam_session(None, contact)


    def _get_selected_contact(self):
        contact_id = self.contact_list_widget.get_selected()
        contact = self.local_contact_store.get_contact(contact_id)
        return contact


    def set_id(self, my_id):
        self.user_info_panel.set_id(my_id)


    def set_disconnected_view(self):
        """
        Hides the view containing the contact list a.o.
        """

        self.login_widget.show()
        self.contact_list_widget.hide()
        #self.empty_notebook_page.hide()
        self.user_info_panel.hide()
        self.separator1.hide()
        self.separator2.hide()
        #self.notebook.hide()
        self.actions_box.hide()
        self.actions_box.set_capabilities(False, False, False)
        self.menubar.set_connected(False)


    def set_connected_view(self):
        """
        Show the connected view, containing a.o. the contact list.
        """

        self.login_widget.hide()
        self.contact_list_widget.show()
        #self.empty_notebook_page.show()
        self.user_info_panel.show()
        self.separator1.show()
        self.separator2.show()
        #self.notebook.show()
        self.actions_box.show()
        self.menubar.set_connected(True)


    def refresh_contact_list(self):
        self.contact_list_widget.set_property('show-offline-contacts',
                self.config_client.get_bool(CONFIG_PATH + '/show-offline-contacts'))
        self.contact_list_widget.set_property('sort-after-name',
                self.config_client.get_bool(CONFIG_PATH + '/sort-after-name'))
        self.contact_list_widget.set_property('show-pictures',
                self.config_client.get_bool(CONFIG_PATH + '/show-pictures'))


    def update_vcard_popup(self, widget, contact_id, cell_right_screen_coord, \
            cell_left_screen_coord, cell_top_screen_coord):
        """
        This function updated the vcard (information, positioni, etc)
        as the same vcard widget is reused and just hidden/shown, moved
        around and updated.
        """

        self.vcard_popup.cancel_show()

        if not self.is_active():
            return

        self.screen_x_right = cell_right_screen_coord
        self.screen_x_left = cell_left_screen_coord
        self.screen_y = cell_top_screen_coord

        do_update = False

        # We never showed the popup before
        if self.previous_id == None:
            do_update = True

        #We are over a different buddy.
        elif contact_id != self.previous_id:
            do_update = True

        # In the case the popup where a buddy got hidden
        # because the mouse pointer left the popup and
        # reenter the buddy row with the mouse pointer,
        # we want to reshow the same popup.

        elif not self.vcard_popup.get_property("visible"):
            self.vcard_popup.delayed_show()

        # In the case we got a new buddy we want to fill
        # the popup with the right data and calculate the
        # new position.
        if do_update == True:
            self.vcard_popup.hide()

            self.vcard_popup.update(contact_id)
            self.vcard_popup.realize()

            popup_width, popup_height = self.vcard_popup.get_size()

            offset = 2 * int(self.vcard_popup.get_border_width())
            popup_width = popup_width - offset

            screen = gtk.gdk.screen_get_default()

            # FIXME: should have tendency to popup on the left side when the window
            # is on the right side of the screen and the other way around

            if (self.screen_x_right - offset + popup_width < screen.get_width()):
                self.vcard_popup.move(self.screen_x_right - offset, self.screen_y - 1)
            else:
                self.vcard_popup.move(self.screen_x_left - popup_width - offset, self.screen_y - 1)

            self.vcard_popup.delayed_show()

            self.previous_id = contact_id


    def hide_vcard_popup(self, widget):
        """
        Function that hides the VCard. The hide is cancelled by 
        another function in the case that the popup gets focus.
        The code for doing this is in the VCardPopup class.
        """

        self.vcard_popup.delayed_hide()


    def on_contact_selected(self, widget, contact_id):
        """
        This callback function is called when a contact is selected
        on the contact list view. 

        It checks the capabilities of the contact and updates the
        action bar accordenly.
        """

        if contact_id == None:
            return

        contact = self.local_contact_store.get_contact(contact_id)
        presence = self.local_contact_store.get_presence(contact_id)
        capabilities = contact.get_capabilities()

        chat = True # Unfortunately this is needed
        talk = (capabilities & tapioca.CAPABILITY_AUDIO)
        conference = (capabilities & tapioca.CAPABILITY_VIDEO)

        if presence == tapioca.PRESENCE_OFFLINE:
            chat, talk, conference = False, False, False

        # FIXME: for test purposes only.
        if contact_id == 'setanta@gmail.com' or \
                contact_id == 'joe.tapioca@gmail.com':
            conference = True
        # FIXME: for test purposes only.

        self.actions_box.set_capabilities(chat, talk, conference)


    def present(self):
        if self.last_position != None:
            x, y = self.last_position
            self.move(x, y)

        gtk.Window.present(self)


    def hide(self):
        self.last_position = self.get_position()
        gtk.Window.hide(self)


    def on_delete_event(self, widget, event=None, user_data=None):
        widget.hide()

        return True


    def quit(self):
        """
        Quit the application.
        """

        self.main_app.quit()

